/*
 * Mug, the ale chat GUI.
 * To help, a good reference is:
 * http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/
 *
 * To use, include mug.h and initialize with
 * loadWindow(void); To add to the chat field call
 * the addLine(char*) function. User input is received
 * with getLine(char* buf, int size);
 * To close out environment use unloadWindow();
 *
 * @see also: initEnv
 */
#include "mug.h"

#define MSGSIZE 3
#define BEGIN 2
#define INDENTSIZE 4
wchat *env;
static char** msg;
static int drawing = 0;
static int msgR, msgC;

static void draw(void);
void msgDialog(char* buf, int format);
void split(char* feed, void(* add)(char*, int), int format);
void clearInput();

void draw(void)
{
	/*
	 * Since there is threading, we don't want to
	 * be making changes at the same time.
	 */
	if (!drawing)
	{
		int r, c;
		int i;
		drawing = 1;
		clear();

		/*
		 * Print the chat messages to the screen.
		 */
		c = BEGIN;
		r = 0;
		for (; r < env->lines; r++)
		{
			move(r, c);
			addstr(env->dialog[(env->first+r) % (env->height)]);
		}

		/*
		 * Separator of to fields.
		 */
		c = BEGIN;
		r = LINES - MSGSIZE - 1;
		for (i = 0; i < COLS / 2; i++)
			mvaddch(r, c++, '_');

		/*
		 * Display the current user message at the bottom
		 * of the screen.
		 */
		c = BEGIN;
		r = LINES-MSGSIZE;
		for (i = 0; r < LINES; r++, i++)
		{
			move(r, c);
			addstr(msg[i]);
		}

		refresh();
		drawing = 0;
	}
}

void animateFall(char* str)
{
	if (str != NULL)
		free(str);
	str = NULL;
}

/*
 * Will feed line to env
 */
void addLine(char* in, int format)
{
	char* feed;
	int i;
	int feedLength = strlen(in)*sizeof(char)+1;
	/*
	 * FIXME: It should only indent once
	 */
	if(format & INDENT)
		feedLength += INDENTSIZE;
	feed = malloc(feedLength);
	if(format & INDENT)
	{
		for(i = 0; i < INDENTSIZE; i++)
			feed[i] = ' ';
		strcpy(feed+INDENTSIZE, in);
	}
	else
		strcpy(feed, in);

	/*
	 * New lines are placed in the start location
	 * and then the start location is moved up one.
	 * This allows for a wrapping array.
	 */
	if (env->lines == env->height)
	{
		animateFall(env->dialog[env->first]);
		env->dialog[env->first] = NULL;

		if (strlen(feed) < env->length)
		{
			env->dialog[env->first] = feed;
			env->first = (env->first+1) % env->height;
		}
		else
			split(feed, addLine, format);
	}
	else
	{
		/* Build up the frame. */
		if (strlen(feed) < env->length)
			env->dialog[(env->first+env->lines) % (env->height)] = feed;
		else
			split(feed, addLine, format);
		env->lines++;
	}

	draw();
}

void split(char* feed, void(* add)(char*, int), int format)
{
	int i;
	char* ptr;
	char* c_feed = malloc(strlen(feed)+1);
	strcpy(c_feed, feed);
	ptr = c_feed + (env->length - 1);
	for (i = 0; i < strlen(c_feed); i++, ptr--)
		if (*ptr == ' ')
		{
			*ptr = '\0';
			ptr++;
			break;
		}
	/*
	 * TODO: Segfault or invalid free is here
	 */
	if (i == strlen(feed))
	{
		char *newptr;
		ptr = c_feed + (env->length - 1);
		newptr = calloc(strlen(ptr) + 1, sizeof(char));
		strcpy(newptr, ptr);
		*ptr = '\0';
		add(c_feed, format &~ INDENT);
		add(newptr, format | INDENT);
		free(newptr);
	}
	else
	{
		add(c_feed, format &~ INDENT);
		add(ptr, format | INDENT);
	}
	free(c_feed);
}

void getLine(char* buf, int size)
{
	int i = 0;
	int typing = 1;
	int c;

	while (typing)
	{
		clearInput();
		switch ((c = getch()))
		{
		case '\n': /* Enter Key, KEY_ENTER doesn't seem to work */
			buf[i++] = '\n';
			buf[i] = '\0';
			typing = 0;
			break;
		case KEY_BACKSPACE:
			if (i > 0)
			{
				buf[--i] = '\0';
				msgDialog(buf, AUTO); 
			}
			break;
		default:
			if (i < (size - 2))
				if (c < 256)
				{
					buf[i++] = c;
					buf[i] = '\0';
					msgDialog(buf, AUTO);
				}
			break;
		}
		draw();
	}
}

void msgDialog(char* buf, int format)
{
	if (strlen(buf) < (env->length-1))
	{
		if (msgR >= MSGSIZE)
		{
			int i = 0;
			free(msg[0]);
			
			/* Do the shuffle! */
			for (i = 0; i < MSGSIZE - 1; i++)
				msg[i] = msg[i + 1];
			msg[i] = malloc(env->length);
			strcpy(msg[i], buf);
		}
		else
			strcpy(msg[msgR++], buf);
	}
	else
	{
		split(buf, msgDialog, format);
	}
}

void clearInput()
{
	int i;
	msgC = 0;
	msgR = 0;
	for (i = 0; i < MSGSIZE; i++)
		if (msg[i])
			memset(msg[i], '\0', sizeof(msg[i]));
}

void loadWindow(void)
{
	int i;
	/* init stdscr <-- Default window pointer */
	initscr();
	/* disable OS buffering, characters sent without enter */
	cbreak();
	/* disable OS echo */
	noecho();
	/* enable function, arrow, ... keys on stdsrc */
	keypad(stdscr, TRUE);
	clear();
	refresh();

	env = malloc(sizeof(wchat));
	env->wnd = stdscr;
	env->length = (COLS * 2) / 3;
	env->height = LINES - MSGSIZE - 1;
	env->lines = 0;
	env->first = 0;
	env->dialog = calloc(env->height, sizeof(char*));

	msgC = 0;
	msgR = 0;
	msg = malloc(MSGSIZE * sizeof(char*));
	for (i = 0; i < MSGSIZE; i++)
		msg[i] = calloc(env->length, sizeof(char));
	draw();
}

void unloadWindow(void)
{
	int counter = 0;
	for (counter = 0; counter < env->lines; counter++)
		free(env->dialog[counter]);
	for (counter = 0; counter < MSGSIZE; counter++)
		free(msg[counter]);
	free(env); env = NULL;
	endwin();
}
